// 普通函数 vs 模板函数：底层机制
// 1. 编译阶段的区别
// 普通函数
void normalFunc(const Shape &s)
{
    s.draw(); // 虚函数调用
}
// 编译成汇编代码（简化版）：
/**
 normalFunc:
    ; 函数序言（保存栈帧等）
    push rbp
    mov rbp, rsp

    ; 获取虚函数表指针（通常在对象的前8字节）
    mov rax, [rdi]      ; rdi 包含 Shape& 参数

    ; 调用虚函数表中的 draw 函数（假设在表的第一个位置）
    call [rax + 8]      ; 运行时查找并调用

    ; 函数尾声
    pop rbp
    ret
 */

// 关键点：
// 编译器生成一份代码
// 包含间接调用（通过虚表）
// 运行时决定调用哪个函数

// 模板函数
template <typename T>
void templateFunc(const T &t)
{
    t.draw(); // 直接调用
}
// ------------------------------------------------------
// 使用
templateFunc(Circle(5.0));
templateFunc(Rectangle(4.0, 3.0));

// 编译成汇编代码（简化版）：
// 为 Circle 生成的实例：

templateFunc<Circle>:
    push rbp
    mov rbp, rsp

    ; 直接调用 Circle::draw, 没有虚表查找
    call Circle::draw   ; 编译时绑定

    pop rbp
    ret

// 为 Rectangle 生成的实例：

templateFunc<Rectangle>:
    push rbp
    mov rbp, rsp

    ; 直接调用 Rectangle::draw, 没有虚表查找
    call Rectangle::draw  ; 编译时绑定

    pop rbp
    ret
// 关键点：
// 编译器为每种类型生成独立代码
// 直接调用（无需虚表）
// 编译时决定调用哪个函数

// ------------------------------------------------
// 2. 链接阶段的区别
// 普通函数
//  在目标文件中生成一个符号 normalFunc
//  所有调用点都链接到这一个函数实现
//  符号在链接时解析一次

// 模板函数
//  为每个实例化生成不同的符号，如 templateFunc<Circle> 和 templateFunc<Rectangle>
//  每个调用点链接到对应的实例化版本
//  多个符号需要在链接时分别解析
//  可能导致代码膨胀（code bloat）

// ------------------------------------------------
// 3. 内存布局的区别
// 使用虚函数的类

内存布局:
+----------------+
| 虚表指针(vptr) | ---> +-------------------+
+----------------+      | &Shape::draw      |
| 成员变量       |      | &Shape::area      |
+----------------+      +-------------------+

//  使用模板的类
内存布局:
+----------------+
| 成员变量       | （没有虚表指针！）
+----------------+

// ------------------------------------------------
// 4. 性能差异
// 1)虚函数调用（动态多态）
//      额外内存开销：每个对象多8字节（64位系统上的vptr）
//      间接调用：需要2次内存访问（加载vptr，然后从虚表加载函数地址）
//      阻碍内联：编译器通常无法内联虚函数调用
//      分支预测困难：CPU难以预测将调用哪个函数

// 2)模板调用（静态多态）
//      无额外内存开销：对象只包含成员变量
//      直接调用：编译时就知道函数地址
//      支持内联：编译器可以内联函数调用
//      分支预测友好：调用路径在编译时确定
